#include "SQLite3Wrapper.hpp"
#include "debug.hpp"
#include <fstream>
#include <functional>
#include <sstream>
#include <stdexcept>

SQLite3Wrapper::SQLite3Wrapper(const std::string &dbFile) : db(nullptr) {
    if (!open(dbFile)) {
        throw std::runtime_error("Failed to open database");
    }
}

SQLite3Wrapper::~SQLite3Wrapper() { close(); }

bool SQLite3Wrapper::open(const std::string &dbFile) {
    debug(), "Opening database: ", dbFile;
    return sqlite3_open(dbFile.c_str(), &db) == SQLITE_OK;
}

void SQLite3Wrapper::close() {
    if (db) {
        sqlite3_close(db);
        db = nullptr;
    }
}

void SQLite3Wrapper::execute(std::string &query) const {
    char *errMsg = nullptr;
    int result = sqlite3_exec(db, query.c_str(), nullptr, nullptr, &errMsg);
    if (result != SQLITE_OK) {
        debug(), "Failed to execute query: ", errMsg;
        sqlite3_free(errMsg);
        throw std::runtime_error("Failed to execute query");
    }
    return;
}

SQLite3Wrapper::queryRes SQLite3Wrapper::query(std::string &query) const {
    sqlite3_stmt *stmt;
    SQLite3Wrapper::queryRes results;

    if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) !=
        SQLITE_OK) {
        debug(), "Failed to prepare statement: ", sqlite3_errmsg(db);
        return results;
    }

    int cols = sqlite3_column_count(stmt);
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        std::vector<std::string> row;
        for (int col = 0; col < cols; ++col) {
            const char *text = (const char *)sqlite3_column_text(stmt, col);
            row.push_back(text ? text : "");
        }
        results.push_back(row);
    }

    sqlite3_finalize(stmt);
    return std::move(results);
}

void SQLite3Wrapper::initializeFromFile(const std::string &sqlFile) {
    std::ifstream file(sqlFile);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open SQL file");
    }
    try {
        executeStream(file);
    } catch (std::exception &e) {
        debug(), "Failed to initialize database: ", e.what();
        throw;
    }
    return;
}

void SQLite3Wrapper::executeStream(std::ifstream &file) {
    std::string line;
    std::string command;
    bool inString = false;

    while (std::getline(file, line)) {
        command += line;
        for (char c : line) {
            if (c == ';' && !inString) {
                try {
                    debug(), "Executing command: ", command;
                    execute(command);
                } catch (std::exception &e) {
                    debug(), "Failed to execute command: ", e.what();
                    throw;
                }
                command.clear();
            } else if (c == '\'') {
                inString = !inString;
            }
        }
    }

    if (!command.empty()) {
        try {
            execute(command);
        } catch (std::exception &e) {
            debug(), "Failed to execute command: ", e.what();
            throw;
        }
    }

    return;
}

void SQLite3Wrapper::queryWithCallback(
    const std::string &query,
    std::function<bool(const std::vector<std::string> &)> callback) {
    sqlite3_stmt *stmt;

    if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) !=
        SQLITE_OK) {
        debug(), "Failed to prepare statement: ", sqlite3_errmsg(db);
        throw std::runtime_error("Failed to prepare statement");
    }

    int cols = sqlite3_column_count(stmt);
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        std::vector<std::string> row;
        for (int col = 0; col < cols; ++col) {
            const char *text = (const char *)sqlite3_column_text(stmt, col);
            row.push_back(text ? text : "");
        }
        if (!callback(row)) {
            break;
        }
    }

    sqlite3_finalize(stmt);
    return;
}